// @Date         2021-11-16
// @Author       Aaron Zhang
// @Description
// @FileName      service.go

package log

import (
	"context"
	"io"
	"log"
	"time"
	"zin/framework/contract"
	"zin/framework/util"
)

type ProviderServiceLog struct {
	Level      contract.LogLevel
	ctxFielder contract.CtxFielder
	formatter  contract.Formatter
	output     []io.Writer
}

func (p *ProviderServiceLog) Panic(ctx context.Context, msg string, fields map[string]interface{}) {
	p.logOutput(contract.LogLevelPanic, ctx, msg, fields)
}

func (p *ProviderServiceLog) Fatal(ctx context.Context, msg string, fields map[string]interface{}) {
	p.logOutput(contract.LogLevelFalter, ctx, msg, fields)
}

func (p *ProviderServiceLog) Error(ctx context.Context, msg string, fields map[string]interface{}) {
	p.logOutput(contract.LogLevelError, ctx, msg, fields)
}

func (p *ProviderServiceLog) Warn(ctx context.Context, msg string, fields map[string]interface{}) {
	p.logOutput(contract.LogLevelWarn, ctx, msg, fields)
}

func (p *ProviderServiceLog) Info(ctx context.Context, msg string, fields map[string]interface{}) {
	p.logOutput(contract.LogLevelInfo, ctx, msg, fields)
}

func (p *ProviderServiceLog) Debug(ctx context.Context, msg string, fields map[string]interface{}) {
	p.logOutput(contract.LogLevelDebug, ctx, msg, fields)
}

func (p *ProviderServiceLog) Trace(ctx context.Context, msg string, fields map[string]interface{}) {
	p.logOutput(contract.LogLevelTrace, ctx, msg, fields)
}

func (p *ProviderServiceLog) SetLevel(level contract.LogLevel) {
	p.Level = level
}

func (p *ProviderServiceLog) SetCtxFielder(handler contract.CtxFielder) {
	p.ctxFielder = handler
}

func (p *ProviderServiceLog) SetFormatter(formatter contract.Formatter) {
	p.formatter = formatter
}

func (p *ProviderServiceLog) SetOutput(writer ...io.Writer) {
	p.output = writer
}

func (p *ProviderServiceLog) logEnable(level contract.LogLevel) bool {
	return level <= p.Level
}

func (p *ProviderServiceLog) logOutput(level contract.LogLevel, ctx context.Context, msg string, fields map[string]interface{}) {
	if !p.logEnable(level) {
		return
	}
	if fields == nil {
		fields = map[string]interface{}{}
	}

	if p.ctxFielder != nil {
		ctxField := p.ctxFielder(ctx)
		fields["ctxField"] = ctxField
	}

	//trace info
	if util.Container.IsBind(contract.ProviderKeyTrace) {
		tracer := util.GetTraceProvider()
		tc := tracer.GetTrace(ctx)
		if tc != nil {
			maps := tracer.ToMap(tc)
			fields["trace"] = maps
		}
	}

	//formatter
	formatter, err := p.formatter(level, time.Now(), msg, fields)
	if err != nil {
		panic(err)
	}

	// 如果是panic级别，则使用log进行panic
	if p.Level == contract.LogLevelPanic {
		log.Panicln(formatter)
	}

	for _, w := range p.output {
		nFormatter := util.ConvertDate2String(time.Now().Unix(), util.DateFormat1+" ") + string(formatter) + "\n"
		w.Write([]byte(nFormatter))
	}
}
